package dmir.reaction.publico.graph.communities;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import org.apache.commons.cli.CommandLine;

import dmir.reaction.utils.Filter;

public class Contained {
		
	public static Set<Community> readCommunities(String filename) throws IOException{
		
		Set<Community> communities = new HashSet<Community>();
		
		FileInputStream fstream = new FileInputStream(filename);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		
		while ((strLine = br.readLine()) != null) {	
			if (!strLine.startsWith("#") && strLine.length()>2) {
				
				Community c = new Community();
																
				String[] data = strLine.split(":");				
				String[] nodes = data[1].split("\\s");
				
				for (int i = 1; i < nodes.length; i++) {					
					c.addNode(Integer.parseInt(nodes[i].trim()));
				}				
				communities.add(c);				
			}
		}
		
		return communities;
	}
	
	public static File[] readFiles(String path) {
		File directory = new File(path);
		File[] files = directory.listFiles(new Filter(".merged"));
		return files;
	}
	
	
	public static void main(CommandLine line, HashMap<Integer, String> entities) throws IOException {
		
		/*
		 * D - graph at time t
		 * E - graph at time t+1
		 * V - union of graph C with E 
		 * 
		 * Read communities of C, E and V
		 *
		 */
		
		String k = line.getOptionValue("k");		
		File[] files = readFiles(line.getOptionValue("path"));		
		Arrays.sort(files, new Comparator() {
	        @Override
	        public int compare(Object f1, Object f2) {
	          return ((File) f1).getName().compareTo(((File) f2).getName());
	        }
	      });
		
		String d = null;
		String e = null;
		String v = null;
		
		int more_than_5 = 0;
		
		for (int i = 0; i < files.length; i++) {
						
			String[] data = files[i].getName().split("_");
						 			
			d = data[0];
			e = data[1].split("\\.")[0];
			v = files[i].getName();
			
			System.out.println(v.split(".merged")[0]);
			
			/* read input files and extracted contained communities */
						
			Set<Community> D = readCommunities(d+".graphml.cfinder_files/k=" + k + "/communities");
			Set<Community> E = readCommunities(e+".graphml.cfinder_files/k=" + k + "/communities");
			Set<Community> V = readCommunities(v+"_files/k=" + k + "/communities");
			
			/*
			System.out.println("D (t)	  = " + d + ".graphml.cfinder_files/k="+k+"/communities \t" + D.size());			
			System.out.println("E (t+1)	  = " + e + ".graphml.cfinder_files/k="+k+"/communities \t" + E.size());
			System.out.println("V (union) = " + v + "_files/k="+k+"/communities \t" + V.size());
			*/
						
			Set<Community> D_contained = new HashSet<Community>();
			Set<Community> E_contained = new HashSet<Community>();
			
			extractContainedCommunities(D,E,V, D_contained, E_contained);
				
			/*
			System.out.println("\nCommunities contained in V:");			
			System.out.println("D in V: " + D_contained.size());
			System.out.println("E in V: " + E_contained.size());
			*/
			
			//System.out.println("\nCalculating communities overlap");
			
			Set<Overlap> overlaps = overlap(D_contained, E_contained, v.split(".merged")[0]);
			
			//System.out.println("overlaps size:" + overlaps.size());
			
			LinkedList<Overlap> overlaps_list = new LinkedList<Overlap>(overlaps);			
			//System.out.println("Sorting overlaps");			
			Collections.sort(overlaps_list, new Comparator<Overlap>() {

				@Override
				public int compare(Overlap o1, Overlap o2) {
					if (o1.overlap>o2.overlap)
						return -1;
					else if (o2.overlap>o1.overlap)
						return 1;
						else return 0;
				}
			});
			
			int count=0;
			for (Overlap overlap : overlaps_list) {
				if (count<5) {
					//System.out.println(overlap.time_span);
					System.out.println("\n\n"+overlap.overlap);
					System.out.println("t: ");
					for (Integer id : overlap.D.clique) {
						System.out.print(entities.get(id) + '\t');
					}
										
					System.out.println("\n\nt+1: ");
					for (Integer id : overlap.E.clique) {
						System.out.print(entities.get(id) + '\t');
					}
					
					count++;
				}
				else break;
			}
			
			System.out.println();
			System.out.println("\n========\n");
		}
	}

	/*
	 * When matching the communities in D and in E, first for every community V^k \in V in the joint system we extract the list of communities (D_i)^k
	 *	\in D and (E_j)^k \in E that are contained in V_k (this means (D_i)^k \in V_k and (E_j)^k \in V_k)
	 *
	 * C is graph at t
	 * d is graph at t+1
	 * v is the union of the graphs
	 */
	
	private static void extractContainedCommunities(Set<Community> c, Set<Community> e, Set<Community> v, Set<Community> c_contained, Set<Community> e_contained) {
		
		for (Community community_v : v) {
			
			for (Community community_c : c) {
				if (community_v.clique.containsAll(community_c.clique)) {
					c_contained.add(community_c);					
				}
			}
			
			for (Community community_e : e) {
				if (community_v.clique.containsAll(community_e.clique)) {
					e_contained.add(community_e);					
				}
			}			
		}
	}
	
	
	/*
	 * Calculate the relative overlap between every possible pair:
	 * 
	 *  C_{i,j} = (D_i intersection E_j) / (D_i union E_j) 
	 *  
	 */
	
	private static Set<Overlap> overlap(Set<Community> d_contained, Set<Community> e_contained, String time_span) {
				
		Set<Overlap> overlaps = new HashSet<Overlap>();
		
		for (Community community_d : d_contained) {
				for (Community community_e : e_contained) {					
					Overlap o = calculateOverlap(community_d, community_e);
					o.time_span = time_span;
					overlaps.add(o);
				}				
			}						
		return overlaps;
	}

	
	static Overlap calculateOverlap(Community community_d, Community community_e) {
		
		/* intersection */
		Set<Integer> intersection = new HashSet<Integer>(community_d.clique);
		intersection.retainAll(community_e.clique);
		
		/* union */
		Set<Integer> union = new HashSet<Integer>(community_d.clique);
		union.addAll(community_e.clique);
				
		/* keep the two communities and the overlap score */
		float result = (float) intersection.size() / (float) union.size();
		Overlap o = new Overlap(community_d, community_e, result);
		
		return o;
	}
}	